package studia.figlewicz.math.library;

import java.util.Calendar;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

public class Utils {
	
	// standaryzacja stopy procentowej
	public static double StandaryzujR(Date data1, Date data2, KonwencjaStopyProcentowej konwencja) {
		int d1 = data1.getDate();
		int d2 = data2.getDate();
		int m1 = data1.getMonth();
		int m2 = data2.getMonth();
		int y1 = data1.getYear();
		int y2 = data2.getYear();
		
		Calendar kalendarz1 = Calendar.getInstance();
		kalendarz1.set(y1,m1,d1);
		Calendar kalendarz2 = Calendar.getInstance();
		kalendarz2.set(y2,m2,d2);

		double ulamekRoku = 0;
		double liczbaDni = 0;
		
		switch (konwencja) {
		case _30_360:
			if (d1 == 31)
				d1 = 30;
			if (d2 == 31)
				if (d1 == 30)
					d2 = 30;
			if (d1 == kalendarz1.getActualMaximum(Calendar.DAY_OF_MONTH))
				if (d2 == kalendarz2.getActualMaximum(Calendar.DAY_OF_MONTH))
					d2 = d1;
			liczbaDni = 360*(y2-y1) + 30*(m2-m1) + (d2-d1);
			ulamekRoku = liczbaDni/360;
			break;
			
		case _30_360E:
			if (d2 == 31)
				d2 = 30;
			liczbaDni = 360*(y2-y1) + 30*(m2-m1) + (d2-d1);
			ulamekRoku = liczbaDni/360;
			break;
			
		case ACT_365:
			int dni1 = 0;
			int dni2 = 0;
			ulamekRoku = 0;
			if (y1 == y2)
				liczbaDni = kalendarz2.DAY_OF_YEAR - kalendarz1.DAY_OF_YEAR;
			else {
				ulamekRoku = y2-y1;
				if (y1 < y2) {
					dni1 = kalendarz1.getActualMaximum(Calendar.DAY_OF_YEAR) - kalendarz1.DAY_OF_YEAR;
					dni2 = kalendarz2.DAY_OF_YEAR;
					liczbaDni = dni1 + dni2; // trzeba miec liczbe dodatnia
				}
				if (y1 > y2) {
					dni2 = kalendarz2.getActualMaximum(Calendar.DAY_OF_YEAR) - kalendarz2.DAY_OF_YEAR;
					dni1 = kalendarz1.DAY_OF_YEAR;
					liczbaDni = -1*(dni1 + dni2); // trzeba miec liczbe ujemna
				}
			}
			ulamekRoku = ulamekRoku + liczbaDni/365;
			break;
			
		case ACT_ACT:
			double dniACT1 = 0;
			double dniACT2 = 0;
			double ulamek1 = 0;
			double ulamek2 = 0;
			ulamekRoku = 0;
			if (y1 == y2) {
				liczbaDni = kalendarz2.DAY_OF_YEAR - kalendarz1.DAY_OF_YEAR;
				ulamekRoku = liczbaDni/kalendarz2.getActualMaximum(Calendar.DAY_OF_YEAR);
			}
			else {
				ulamekRoku = y2-y1;
				if (y1 < y2) {
					dniACT1 = kalendarz1.getActualMaximum(Calendar.DAY_OF_YEAR) - kalendarz1.DAY_OF_YEAR;
					dniACT2 = kalendarz2.DAY_OF_YEAR;
					ulamek1 = dniACT1 / kalendarz1.getActualMaximum(Calendar.DAY_OF_YEAR);
					ulamek2 = dniACT2 / kalendarz2.getActualMaximum(Calendar.DAY_OF_YEAR);
				}
				if (y1 > y2) {
					dniACT2 = kalendarz2.getActualMaximum(Calendar.DAY_OF_YEAR) - kalendarz2.DAY_OF_YEAR;
					dniACT1 = kalendarz1.DAY_OF_YEAR;
					ulamek1 = -1 * dniACT1 / kalendarz1.getActualMaximum(Calendar.DAY_OF_YEAR);
					ulamek2 = -1 * dniACT2 / kalendarz2.getActualMaximum(Calendar.DAY_OF_YEAR);
				}
			}
			ulamekRoku = ulamekRoku + ulamek1 + ulamek2;
			break;
		}
		return ulamekRoku;
	}
	
	public static double getYearFractionStandard(Date data1, Date data2) {
		double resoult = StandaryzujR(data1, data2, KonwencjaStopyProcentowej._30_360);
		return resoult;
	}
	
	public static double getYearFractionStandard(long miniseconds) {
		double yearMiliseconds = (double)getYearMilisecondsStandard();
		double yearFraction = miniseconds/(double)yearMiliseconds;
		return yearFraction;
	}

	public static long getDayMiliseconds() {
		long dayMiliseconds = 24; // godziny
		dayMiliseconds = dayMiliseconds * 60; // minuty
		dayMiliseconds = dayMiliseconds * 60; // sekundy
		dayMiliseconds = dayMiliseconds * 1000; // minilekundy
		return dayMiliseconds;
	}
	
	public static double getYearFractionStandardDays(long days) {
		double yearDays = 360.0;
		return days/yearDays;
	}
	
	public static long getYearMilisecondsStandard() {
		long dayMiliseconds = getDayMiliseconds();
		long yearMiliseconds = dayMiliseconds * 365;
		return yearMiliseconds;
	}
	
	public static long getDayMiliseconds(double dayFraction) {
		long dayMiliseconds = getDayMiliseconds();
		return (long)(dayMiliseconds * dayFraction);
	}
	
	public static Date getNextDateMiliseconds(Date data, long miliseconds) {
		return new Date(data.getTime() + miliseconds);
	}
	
	public static Date getNextDateDayFraction(Date data, double dayFraction) {
		long miliseconds = getDayMiliseconds(dayFraction);
		return getNextDateMiliseconds(data,miliseconds);
	}
	
	public static double interpolation(Date day, MetodaInterpolacji method, Date d1, Date d2, double val1, double val2) {
		double resoult;
		
		switch (method) {
		case STALA: {
				resoult = val1;
				break;
			}
		case LINIOWA: {
				long dl = d2.getTime() - d1.getTime();
				long delta = day.getTime() - d1.getTime();
				double waga = delta/(double)dl;
	
				resoult = val1 + (val2-val1)*waga;
				break;
			}
		default:
			resoult = interpolation(day, MetodaInterpolacji.LINIOWA, d1, d2, val1, val2);
		}
		
		return resoult;
	}
	
	public static Konwencja getStandardKonwencja() {
		Konwencja resoult = new Konwencja();
		resoult.setNominal(null);
		resoult.setTerminRozpoczecia(new Termin(0,0,0,0));
		resoult.setTerminZakonczenia(new Termin(0,0,0,0));
		resoult.setTerminRozliczenia(new Termin(0,0,0,0));
		resoult.setZmianDniRoboczych(KonwencjaDniRoboczych.NF);
		resoult.setStopaProcentowa(KonwencjaStopyProcentowej._30_360);
		resoult.setKalendarz(null);
		return resoult;
	}
	
	public static Double wartoscWygladzona(LinkedHashMap<Date,Double> mapa, Date punkt, MetodaInterpolacji metoda) {
		if (mapa.isEmpty())
			return null;
		Double resoult = null;
		
		Set<Date> sasiednie = get2Sasiednie(mapa.keySet(),punkt);
		
		if (sasiednie == null)
			return null;
		
		Object[] ds = sasiednie.toArray();
		
		Date d1 = (Date)ds[0];
		if (sasiednie.size() == 1)
			return mapa.get(d1);
		Date d2 = (Date)ds[1];
		if (d1.after(d2)) {
			Date temp = d1;
			d1 = d2;
			d2 = temp;
		}
		
		resoult = new Double(interpolation(punkt,metoda,d1,d2,mapa.get(d1),mapa.get(d2)));
		return resoult;
	}
	
	public static Set<Date> get2Sasiednie(Set<Date> punkty, Date ref) {
		if (punkty.isEmpty())
			return null;
		Set<Date> resoult = new LinkedHashSet<Date>();
		
		if (punkty.contains(ref)) {
			resoult.add(ref);
			return resoult;
		}
		
		boolean czyWiekszy = false;
		boolean czyMniejszy = false;
		Date wiekszy = null;
		Date mniejszy = null;
		for (Date d : punkty) {
			
			if (d.before(ref)) {
				if (!czyMniejszy) {
					mniejszy = d;
					czyMniejszy = true;
				}
				
				if (d.after(mniejszy))
					mniejszy = d;
			}
			
			else if (d.after(ref)) {
				if (!czyWiekszy) {
					wiekszy = d;
					czyWiekszy = true;
				}
				
				if (d.before(wiekszy))
					wiekszy = d;
			}
		}

		
		if (czyMniejszy)
			resoult.add(mniejszy);
		if (czyWiekszy)
			resoult.add(wiekszy);
		
		return resoult;
	}
	
	public static Date getDefaultDate() {
		Date resoult = new Date(1,0,1);
		return resoult;
	}
	
	public static void sortuj(LinkedHashMap<Date,Object> mapa) {
		LinkedHashMap<Date,Object> temp = new LinkedHashMap<Date,Object>();
		
		for (Date d : mapa.keySet()) {
			temp.put(d, mapa.get(d));
		}
		mapa.clear();
		
		while (!temp.isEmpty()) {
			Date min = null;
			for (Date d : temp.keySet()) {
				if (min == null)
					min = d;
				else if (d.before(min))
					min = d;
			}
			mapa.put(min, temp.get(min));
			temp.remove(min);
		}
	}
	public static void sortuj(LinkedHashSet<Date> daty) {
		LinkedHashSet<Date> temp = new LinkedHashSet<Date>();
		
		for (Date d : daty) {
			temp.add(d);
		}
		daty.clear();
		
		while (!temp.isEmpty()) {
			Date min = null;
			for (Date d : temp) {
				if (min == null)
					min = d;
				else if (d.before(min))
					min = d;
			}
			daty.add(min);
			temp.remove(min);
		}
	}
	public static Date getOstatniPrzed(Map<Date,Object> mapa, Date punkt) {
		Date resoult = null;
		
		for (Date d : mapa.keySet()) {
			if (d.before(punkt)) {
				if (resoult == null) {
					resoult = d;
				}
				if (d.after(resoult))
					resoult = d;
			}
		}
		return resoult;
	}
	public static Date getPierwszyPo(Map<Date,Object> mapa, Date punkt) {
		Date resoult = null;
		
		for (Date d : mapa.keySet()) {
			if (d.after(punkt)) {
				if (resoult == null) {
					resoult = d;
				}
				if (d.before(resoult))
					resoult = d;
			}
		}
		return resoult;
	}
}
